CREATE
與 CREATE2
在 Ethereum 虛擬機 (EVM) 中,合約的創建是一個核心功能。這允許開發者部署新的智能合約到鏈上。EVM 提供了兩種主要的合約創建指令:CREATE
和 CREATE2
。這兩者都允許創建新的合約,但它們的工作方式略有不同。
昨天解釋了簡單的系統指令的範例,今天就來實際實現 code
#
# System
#
CREATE = 0xF0
CREATE2 = 0xF5
CREATE
指令CREATE
指令允許用戶創建新的智能合約。它使用發送者的地址和其當前的 nonce 來確定新合約的地址。當合約被創建時,它的初始化代碼會被執行,並且任何返回的代碼會被保存為合約的主體代碼。
def create(self):
value = self.evm.stack.pop()
mem_position = self.evm.stack.pop()
length = self.evm.stack.pop()
init_code = self.evm.memory[mem_position: mem_position + length]
creator = self.getAccount(self.txn.origin)
creator['balance'] -= value
contract_addr_bytes = keccak(self.txn.thisAddr.encode() + str(creator['nonce']).encode()).digest()
new_contract_address = '0x' + contract_addr_bytes[-20:].hex()
creator['nonce'] += 1
self.evm_account[new_contract_address] = {
'balance': value,
'nonce': 0,
'storage': bytearray(),
'code': init_code
}
self.evm.stack.append(int(new_contract_address, 16))
CREATE2
指令CREATE2
指令是一個較新的添加,它允許用戶以一種可以預測的方式創建合約。這是通過使用發送者的地址、一個鹽值和初始化代碼的哈希來確定新合約的地址來實現的。這意味著,只要這些參數保持不變,新合約的地址就可以被預測。
在這邊先列出 create2 和 create 最大的不一致之處
init_code_hash = keccak(init_code).digest()
data_to_hash = b'\\xff' + self.txn.thisAddr.encode() + str(salt).encode() + init_code_hash
contract_addr_bytes = keccak(data_to_hash).digest()
new_contract_address = '0x' + contract_addr_bytes[-20:].hex()
在底下列出整個 CREATE
和 CREATE2
的詳細實現:
import random
from eth_hash.auto import keccak
class System:
def __init__(self, evm, opCode, txn) -> None:
self.evm = evm
self.evm_account = {}
self.txn = txn
def create(self):
value = self.evm.stack.pop()
mem_position = self.evm.stack.pop()
length = self.evm.stack.pop()
init_code = self.evm.memory[mem_position: mem_position + length]
creator = self.getAccount(self.txn.origin)
creator['balance'] -= value
contract_addr_bytes = keccak(self.txn.thisAddr.encode() + str(creator['nonce']).encode()).digest()
new_contract_address = '0x' + contract_addr_bytes[-20:].hex()
creator['nonce'] += 1
self.evm_account[new_contract_address] = {
'balance': value,
'nonce': 0,
'storage': bytearray(),
'code': init_code
}
self.evm.stack.append(int(new_contract_address, 16))
def create2(self, salt):
value = self.evm.stack.pop()
mem_position = self.evm.stack.pop()
length = self.evm.stack.pop()
init_code = self.evm.memory[mem_position: mem_position + length]
creator = self.getAccount(self.txn.origin)
creator['balance'] -= value
init_code_hash = keccak(init_code).digest()
data_to_hash = b'\\xff' + self.txn.thisAddr.encode() + str(salt).encode() + init_code_hash
contract_addr_bytes = keccak(data_to_hash).digest()
new_contract_address = '0x' + contract_addr_bytes[-20:].hex()
creator['nonce'] += 1
self.evm_account[new_contract_address] = {
'balance': value,
'nonce': 0,
'storage': bytearray(),
'code': init_code
}
self.evm.stack.append(int(new_contract_address, 16))
def getAccount(self, address):
if address not in self.evm_account:
self.evm_account[address] = {
'balance': random.randint(10, 50),
'nonce': random.randint(0, 50),
}
return self.evm_account[address]
在這邊,因為是單純實現 evm create的部分,所以 account的部分就以空array 作弊過去哈哈,這邊大致介紹了系統指令的 create 和 create2 作法,如果有任何問題都可以提出問我。